# -*- tcl -*-
########################################################################
# BigFloat for Tcl
# Copyright (C) 2003-2005  ARNOLD Stephane
# This software is covered by tcllib's license terms.
# See the "license.terms" provided with tcllib.
########################################################################

# -------------------------------------------------------------------------

source [file join \
	[file dirname [file dirname [file join [pwd] [info script]]]] \
	devtools testutilities.tcl]

testsNeedTcl     8.5
testsNeedTcltest 1.0

support {
    useLocal math.tcl math
}
testing {
    useLocal bigfloat2.tcl math::bigfloat
}

# -------------------------------------------------------------------------

namespace import ::math::bigfloat::*

# -------------------------------------------------------------------------

proc assert {name version code result} {
    tcltest::test bigfloat-$name-$version "Some integer computations related to command $name" {uplevel 1 $code} $result
    return
}

interp alias {} zero {} string repeat 0
# S.ARNOLD 08/01/2005
# trying to set the precision of the comparisons to 15 digits
set old_precision $::tcl_precision
set ::tcl_precision 15
proc Zero {x} {
    global tcl_precision
    set x [expr {abs($x)}]
    set epsilon 10.0e-$tcl_precision
    return [expr {$x<$epsilon}]
}

proc fassert {name version code result} {
    #puts -nonewline $version,
    set tested [uplevel 1 $code]
    if {[Zero $tested]} {
        tcltest::test bigfloat-$name-$version "Some floating-point computations related to command $name" {return [Zero $result]} 1
        return
    }
    set resultat [Zero [expr {($tested-$result)/((abs($tested)>1)?($tested):1.0)}]]
    tcltest::test bigfloat-$name-$version "Some floating-point computations related to command $name" {return $resultat} 1
    return
}
# preprocessing is done
#set n


######################################################
# Begin testsuite
######################################################

proc testSuite {} {


    # adds 999..9 and 1 -> 1000..0
    for {set i 1} {$i<15} {incr i} {
        assert add 1.0 {tostr [add \
                    [fromstr [string repeat 999 $i]] [fromstr 1]]
        } 1[string repeat 000 $i]
    }
    # sub 1000..0 1 -> 999..9
    for {set i 1} {$i<15} {incr i} {
        assert sub 1.1 {tostr [sub [fromstr 1[string repeat 000 $i]] [fromstr 1]]} \
                [string repeat 999 $i]
    }
    # mul 10001000..1000 with 1..9
    for {set i 1} {$i<15} {incr i} {
        foreach j {1 2 3 4 5 6 7 8 9} {
            assert mul 1.2 {tostr [mul [fromstr [string repeat 1000 $i]] [fromstr $j]]} \
                    [string repeat ${j}000 $i]
        }
    }
    # div 10^8 by 1 .. 9
    for {set i 1} {$i<=9} {incr i} {
        assert div 1.3 {tostr [div [fromstr 100000000] [fromstr $i]]} [expr {wide(100000000)/$i}]
    }


    # 10^8 modulo 1 .. 9
    for {set i 1} {$i<=9} {incr i} {
        assert mod 1.4 {tostr [mod [fromstr 100000000] [fromstr $i]]} [expr {wide(100000000)%$i}]
    }

    ################################################################################
    # fromstr problem with octal exponents
    ################################################################################
    fassert fromstr 2.0 {todouble [fromstr 1.0e+099]} 1.0e+099
    fassert fromstr 2.0a {todouble [fromstr 1.0e99]} 1.0e99
    fassert fromstr 2.0b {todouble [fromstr 1.0e-99]} 1.0e-99
    fassert fromstr 2.0c {todouble [fromstr 1.0e-099]} 1.0e-99


    ################################################################################
    # fromdouble with precision
    ################################################################################
    assert fromdouble 2.1 {tostr [ceil [fromdouble 1.0e99 100]]} 1[zero 99]
    assert fromdouble 2.1a {tostr [fromdouble 1.11 3]} 1.11
    assert fromdouble 2.1b {tostr [fromdouble +1.11 3]} 1.11
    assert fromdouble 2.1c {tostr [fromdouble -1.11 3]} -1.11
    assert fromdouble 2.1d {tostr [fromdouble +01.11 3]} 1.11
    assert fromdouble 2.1e {tostr [fromdouble -01.11 3]} -1.11
    # more to come...
    fassert fromdouble 2.1f {compare [fromdouble [expr {atan(1.0)*4}]] [pi $::tcl_precision]} 0

    ################################################################################
    # abs()
    ################################################################################
    proc absTest {version x {int 0}} {
        if {!$int} {
            fassert abs $version {
                tostr [abs [fromstr $x]]
            } [expr {abs($x)}]
        } else {
            assert abs $version {
                tostr [abs [fromstr $x]]
            } [expr {($x<0)?(-$x):$x}]
        }

    }
    absTest 2.2a 1.000
    absTest 2.2b -1.000
    absTest 2.2c -0.10
    absTest 2.2d 0 1
    absTest 2.2e 1 1
    absTest 2.2f 10000 1
    absTest 2.2g -1 1
    absTest 2.2h -10000 1
    rename absTest ""

    ################################################################################
    # opposite
    ################################################################################
    proc oppTest {version x {int 0}} {
        if {$int} {
            assert opp $version {tostr [opp [fromstr $x]]} [expr {-$x}]
        } else {
            fassert opp $version {tostr [opp [fromstr $x]]} [expr {-$x}]
        }

    }
    oppTest 2.3a 1.00
    oppTest 2.3b -1.00
    oppTest 2.3c 0.10
    oppTest 2.3d -0.10
    oppTest 2.3e 0.00
    oppTest 2.3f 1 1
    oppTest 2.3g -1 1
    oppTest 2.3h 0 1
    oppTest 2.3i 100000000 1
    oppTest 2.3j -100000000 1
    rename oppTest ""

    ################################################################################
    # equal
    ################################################################################
    proc equalTest {x y} {
        equal [fromstr $x] [fromstr $y]
    }
    assert equal 2.4a {equalTest 0.0 0.1} 1
    assert equal 2.4b {equalTest 0.00 0.10} 0
    assert equal 2.4c {equalTest 0.0 -0.1} 1
    assert equal 2.4d {equalTest 0.00 -0.10} 0

    rename equalTest ""
    ################################################################################
    # compare
    ################################################################################
    proc compareTest {x y} {
        compare [fromstr $x] [fromstr $y]
    }
    assert cmp 2.5a {compareTest 0.00 0.10} -1
    assert cmp 2.5b {compareTest 0.1 0.4} -1
    assert cmp 2.5c {compareTest 0.0 -1.0} 1
    assert cmp 2.5d {compareTest -1.0 0.0} -1
    assert cmp 2.5e {compareTest 0.00 0.10} -1

    # cleanup
    rename compareTest ""

    ################################################################################
    # round
    ################################################################################
    proc roundTest {version x rounded} {
        assert round $version {tostr [round [fromstr $x]]} $rounded
    }
    roundTest 2.6a 0.10 0
    roundTest 2.6b 0.0 0
    roundTest 2.6c 0.50 1
    roundTest 2.6d 0.40 0
    roundTest 2.6e 1.0 1
    roundTest 2.6d -0.40 0
    roundTest 2.6e -0.50 -1
    roundTest 2.6f -1.0 -1
    roundTest 2.6g -1.50 -2
    roundTest 2.6h 1.50 2
    roundTest 2.6i 0.49 0
    roundTest 2.6j -0.49 0
    roundTest 2.6k 1.49 1
    roundTest 2.6l -1.49 -1


    # cleanup
    rename roundTest ""

    ################################################################################
    # floor
    ################################################################################
    proc floorTest {version x} {
        assert floor $version {tostr [floor [fromstr $x]]} [expr {int(floor($x))}]
    }
    floorTest 2.7a 0.10
    floorTest 2.7b 0.90
    floorTest 2.7c 1.0
    floorTest 2.7d -0.10
    floorTest 2.7e -1.0

    # cleanup
    rename floorTest ""

    ################################################################################
    # ceil
    ################################################################################
    proc ceilTest {version x} {
        assert ceil $version {tostr [ceil [fromstr $x]]} [expr {int(ceil($x))}]
    }
    ceilTest 2.8a 0.10
    ceilTest 2.8b 0.90
    ceilTest 2.8c 1.0
    ceilTest 2.8d -0.10
    ceilTest 2.8e -1.0
    ceilTest 2.8f 0.0

    # cleanup
    rename ceilTest ""

    ################################################################################
    # BigInt to BigFloat conversion
    ################################################################################
    proc convTest {version x {decimals 1}} {
        assert int2float $version {tostr [int2float [fromstr $x] $decimals]} \
                $x.[string repeat 0 [expr {$decimals-1}]]
    }
    set subversion 0
    foreach decimals {1 2 5 10 100} {
        set version 2.9.$subversion
        fassert int2float $version.0 {tostr [int2float [fromstr 0] $decimals]} 0.0
        convTest $version.1 1 $decimals
        convTest $version.2 5 $decimals
        convTest $version.3 5000000000 $decimals
        incr subversion
    }
    #cleanup
    rename convTest ""

    ################################################################################
    # addition
    ################################################################################
    proc addTest {version x y} {
        fassert add $version {todouble [add [fromstr $x] [fromstr $y]]} [expr {$x+$y}]
    }
    addTest 3.0a 1.00 2.00
    addTest 3.0b -1.00 2.00
    addTest 3.0c 1.00 -2.00
    addTest 3.0d -1.00 -2.00
    addTest 3.0e 0.00 1.00
    addTest 3.0f 0.00 -1.00
    addTest 3.0g 1 2.00
    addTest 3.0h 1 -2.00
    addTest 3.0i 0 1.00
    addTest 3.0j 0 -1.00
    addTest 3.0k 2.00 1
    addTest 3.0l -2.00 1
    addTest 3.0m 1.00 0
    addTest 3.0n -1.00 0
    #cleanup
    rename addTest ""

    ################################################################################
    # substraction
    ################################################################################
    proc subTest {version x y} {
        fassert sub $version {todouble [sub [fromstr $x] [fromstr $y]]} [expr {$x-$y}]
    }
    subTest 3.1a 1.00 2.00
    subTest 3.1b -1.00 2.00
    subTest 3.1c 1.00 -2.00
    subTest 3.1d -1.00 -2.00
    subTest 3.1e 0.00 1.00
    subTest 3.1f 0.00 -1.00
    subTest 3.1g 1 2.00
    subTest 3.1h 1 -2.00
    subTest 3.1i 0 2.00
    subTest 3.1j 0 -2.00
    subTest 3.1k 2 0.00
    subTest 3.1l 2.00 1
    subTest 3.1m 1.00 2
    subTest 3.1n -1.00 1
    subTest 3.1o 0.00 2
    subTest 3.1p 2.00 0
    # cleanup
    rename subTest ""

    ################################################################################
    # multiplication
    ################################################################################
    proc mulTest {version x y} {
        fassert mul $version {todouble [mul [fromstr $x] [fromstr $y]]} [expr {$x*$y}]
    }
    proc mulInt {version x y} {
        mulTest $version.0 $x $y
        mulTest $version.1 $y $x
    }
    mulTest 3.2a 1.00 2.00
    mulTest 3.2b -1.00 2.00
    mulTest 3.2c 1.00 -2.00
    mulTest 3.2d -1.00 -2.00
    mulTest 3.2e 0.00 1.00
    mulTest 3.2f 0.00 -1.00
    mulTest 3.2g 1.00 10.0
    mulInt 3.2h 1 2.00
    mulInt 3.2i 1 -2.00
    mulInt 3.2j 0 2.00
    mulInt 3.2k 0 -2.00
    mulInt 3.2l 10 2.00
    mulInt 3.2m 10 -2.00
    mulInt 3.2n 1 0.00


    # cleanup
    rename mulTest ""
    rename mulInt ""

    ################################################################################
    # division
    ################################################################################
    proc divTest {version x y} {
        fassert div $version {
            string trimright [todouble [div [fromstr $x] [fromstr $y]]] 0
        } [string trimright [expr {$x/$y}] 0]
    }


    divTest 3.3a 1.00 2.00
    divTest 3.3b 2.00 1.00
    divTest 3.3c -1.00 2.00
    divTest 3.3d 1.00 -2.00
    divTest 3.3e 2.00 -1.00
    divTest 3.3f -2.00 1.00
    divTest 3.3g -1.00 -2.00
    divTest 3.3h -2.00 -1.00
    divTest 3.3i 0.0 1.0
    divTest 3.3j 0.0 -1.0

    # cleanup
    rename divTest ""

    ################################################################################
    # rest of the division
    ################################################################################
    proc modTest {version x y} {
        fassert mod $version {
            todouble [mod [fromstr $x] [fromstr $y]]
        } [expr {fmod($x,$y)}]
    }

    modTest 3.4a 1.00 2.00
    modTest 3.4b 2.00 1.00
    modTest 3.4c -1.00 2.00
    modTest 3.4d 1.00 -2.00
    modTest 3.4e 2.00 -1.00
    modTest 3.4f -2.00 1.00
    modTest 3.4g -1.00 -2.00
    modTest 3.4h -2.00 -1.00
    modTest 3.4i 0.0 1.0
    modTest 3.4j 0.0 -1.0

    modTest 3.4k 1.00 2
    modTest 3.4l 2.00 1
    modTest 3.4m -1.00 2
    modTest 3.4n -2.00 1
    modTest 3.4o 0.0 1
    modTest 3.4p 1.50 1

    # cleanup
    rename modTest ""

    ################################################################################
    # divide a BigFloat by an integer
    ################################################################################
    proc divTest {version x y} {
        fassert div $version {todouble [div [fromstr $x] [fromstr $y]]} \
            [expr {double(round(1000*$x/$y))/1000.0}]
    }
    set subversion 0
    foreach a {1.0000 -1.0000} {
        foreach b {2 3} {
            divTest 3.5.$subversion $a $b
            incr subversion
        }
    }

    # cleanup
    rename divTest ""

    ################################################################################
    # pow : takes a float to an integer power (>0)
    ################################################################################
    proc powTest {version x y {int 0}} {
        if {!$int} {
            fassert pow $version {todouble [pow [fromstr $x 14] [fromstr $y]]}\
                    [expr [join [string repeat "[string trimright $x 0] " $y] *]]
        } else  {
            assert pow $version {tostr [pow [fromstr $x] [fromstr $y]]}\
                    [expr [join [string repeat "$x " $y] *]]
        }
    }
    set subversion 0
    foreach a {1 -1 2 -2 5 -5} {
        foreach b {2 3 7 16} {
            powTest 3.6.$subversion $a. $b
            incr subversion
        }
    }
    set subversion 0
    foreach a {1 2 3} {
        foreach b {2 3 5 8} {
            powTest 3.7.$subversion $a $b 1
            incr subversion
        }
    }

    # cleanup
    rename powTest ""


    ################################################################################
    # pi constant and angles conversion
    ################################################################################
    fassert pi 3.8.0 {todouble [pi 16]} [expr {atan(1)*4}]
    # converts Pi -> 180°
    fassert rad2deg 3.8.1 {todouble [rad2deg [pi 20]]} 180.0
    # converts 180° -> Pi
    fassert deg2rad 3.8.2 {todouble [deg2rad [fromstr 180.0 20]]} [expr {atan(1.0)*4}]


    ################################################################################
    # iszero : the precision is too small to determinate the number
    ################################################################################

    assert iszero 4.0a {iszero [fromstr 0]} 1
    assert iszero 4.0b {iszero [fromstr 0.0]} 1
    assert iszero 4.0c {iszero [fromstr 1]} 0
    assert iszero 4.0d {iszero [fromstr 1.0]} 0
    assert iszero 4.0e {iszero [fromstr -1]} 0
    assert iszero 4.0f {iszero [fromstr -1.0]} 0

    ################################################################################
    # sqrt : square root
    ################################################################################
    proc sqrtTest {version x} {
        fassert sqrt $version {todouble [sqrt [fromstr $x 18]]} [expr {sqrt($x)}]
    }
    sqrtTest 4.1a 1.
    sqrtTest 4.1b 0.001
    sqrtTest 4.1c 0.004
    sqrtTest 4.1d 4.

    # cleanup
    rename sqrtTest ""


    ################################################################################
    # expTest : exponential function
    ################################################################################
    proc expTest {version x} {
        fassert exp $version {todouble [exp [fromstr $x 17]]} [expr {exp($x)}]
    }

    expTest 4.2a 1.
    expTest 4.2b 0.001
    expTest 4.2c 0.004
    expTest 4.2d 40.
    expTest 4.2e -0.001

    # cleanup
    rename expTest ""

    ################################################################################
    # logTest : logarithm
    ################################################################################
    proc logTest {version x} {
        fassert log $version {todouble [log [fromstr $x 17]]} [expr {log($x)}]
    }

    logTest 4.3a 1.0
    logTest 4.3b 0.001
    logTest 4.3c 0.004
    logTest 4.3d 40.
    logTest 4.3e 1[zero 10].0

    # cleanup
    rename logTest ""

    ################################################################################
    # cos & sin : trigonometry
    ################################################################################
    proc cosEtSin {version quartersOfPi} {
        set x [div [mul [pi 18] [fromstr $quartersOfPi]] [fromstr 4]]
        #fassert cos {todouble [cos $x]} [expr {cos(atan(1)*$quartersOfPi)}]
        #fassert sin {todouble [sin $x]} [expr {sin(atan(1)*$quartersOfPi)}]
        fassert cos $version.0 {todouble [cos $x]} [expr {cos([todouble $x])}]
        fassert sin $version.1 {todouble [sin $x]} [expr {sin([todouble $x])}]
    }

    fassert cos 4.4.0.0 {todouble [cos [fromstr 0. 17]]} [expr {cos(0)}]
    fassert sin 4.4.0.1 {todouble [sin [fromstr 0. 17]]} [expr {sin(0)}]
    foreach i {1 2 3 4 5 6 7 8} {
        cosEtSin 4.4.$i $i
    }


    # cleanup
    rename cosEtSin ""

    ################################################################################
    # tan & cotan : trigonometry
    ################################################################################
    proc tanCotan {version i} {
        upvar pi pi
        set x [div [mul $pi [fromstr $i]] [fromstr 10]]
        set double [expr {atan(1)*(double($i)*0.4)}]
        fassert cos $version.0 {todouble [cos $x]} [expr {cos($double)}]
        fassert sin $version.1 {todouble [sin $x]} [expr {sin($double)}]
        fassert tan $version.2 {todouble [tan $x]} [expr {tan($double)}]
        fassert cotan $version.3 {todouble [cotan $x]} [expr {double(1.0)/tan($double)}]
    }

    set pi [pi 20]
    set subversion 0
    foreach i {1 2 3 6 7 8 9} {
        tanCotan 4.5.$subversion $i
        incr subversion
    }


    # cleanup
    rename tanCotan ""


    ################################################################################
    # atan , asin & acos : trigonometry (inverse functions)
    ################################################################################
    proc atanTest {version x} {
        set f [fromstr $x 20]
        fassert atan $version.0 {todouble [atan $f]} [expr {atan($x)}]
        if {abs($x)<=1.0} {
            fassert acos $version.1 {todouble [acos $f]} [expr {acos($x)}]
            fassert asin $version.2 {todouble [asin $f]} [expr {asin($x)}]
        }
    }
    set subversion 0
    atanTest 4.6.0.0 0.0
    foreach i {1 2 3 4 5 6 7 8 9} {
        atanTest 4.6.1.$subversion 0.$i
        atanTest 4.6.2.$subversion $i.0
        atanTest 4.6.3.$subversion -0.$i
        atanTest 4.6.4.$subversion -$i.0
        incr subversion
    }

    # cleanup
    rename atanTest ""

    ################################################################################
    # cosh , sinh & tanh : hyperbolic functions
    ################################################################################
    proc hyper {version x} {
        set f [fromstr $x 18]
        fassert cosh $version.0 {todouble [cosh $f]} [expr {cosh($x)}]
        fassert sinh $version.1 {todouble [sinh $f]} [expr {sinh($x)}]
        fassert tanh $version.2 {todouble [tanh $f]} [expr {tanh($x)}]
    }

    hyper 4.7.0 0.0
    set subversion 0
    foreach i {1 2 3 4 5 6 7 8 9} {
        hyper 4.7.1.$subversion 0.$i
        hyper 4.7.2.$subversion $i.0
        hyper 4.7.3.$subversion -0.$i
        hyper 4.7.4.$subversion -$i.0
    }

    # cleanup
    rename hyper ""

	################################################################################
	# tostr with -nosci option
	################################################################################
	set version 5.0
	fassert tostr-nosci $version.0 {tostr -nosci [fromstr 23450.e+7]} 234500000000.
	fassert tostr-nosci $version.1 {tostr -nosci [fromstr 23450.e-7]} 0.002345
	fassert tostr-nosci $version.2 {tostr -nosci [fromstr 23450000]} 23450000.
	fassert tostr-nosci $version.3 {tostr -nosci [fromstr 2345.0]} 2345.

	################################################################################
	# tests for isInt - ticket 3309165
	################################################################################
	assert isInt $version.0 {isInt 12345678901234} 1
	assert isInt $version.1 {isInt 12345678901234.0} 0
	assert isInt $version.1 {isInt not-a-number} 0
}

testSuite
################################################################################
# end of testsuite for bigfloat 2.0
################################################################################
# cleanup global procs
rename assert ""
rename fassert ""
rename Zero ""

testsuiteCleanup

set ::tcl_precision $old_precision


